package com.bonc.excel.dto.response;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.fastjson.JSONObject;
import com.bonc.excel.utils.ListPageUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import javafx.application.Application;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Author: wujing
 * @Date: 2024/9/6 10:04
 * @Description: TODO
 **/
@Slf4j
public class DynamicExcelDemo {
    //@Autowired
    static Application application;

    public static void main(String[] args) throws Exception {

        // Returns the default class pool.
        ClassPool classPool = ClassPool.getDefault();

        //Reads a class file from the source and returns a reference to the <code>CtClass</code>
        CtClass testDataClassOfCtClass = classPool.get("com.bonc.intelligent.dto.response.RspVo");
        // Returns a class file for this class.
        ClassFile testDataClassFile = testDataClassOfCtClass.getClassFile();
        // Returns a constant pool table.
        ConstPool constPool = testDataClassFile.getConstPool();

      //  List<String> fields = Lists.newArrayList("tom","miki","domi");

        //数据20个
        List<String> datas = setData();
        //
        LinkedHashMap<String, List<String>> colTitleMaps = getColTitle(setColData());
        //行数
        List<String>  rows= Lists.newArrayList("数据","政企","公众","管理","合计");
        List<String> fields = Lists.newArrayList();
        for (int k = 0; k < colTitleMaps.size()+1; k++) {
            String fieldName = "name"+k;
            fields.add(fieldName);
            List<String> titleNames = Lists.newArrayList();
            if(k==0){
                titleNames.add( "处室\\分项");
            }else{
                titleNames = colTitleMaps.get(String.valueOf(k));
            }
            //设置字段
            extracted(classPool, fieldName, testDataClassOfCtClass, constPool,titleNames);
        }

       // testDataClassOfCtClass.writeFile();
       // testDataClassOfCtClass.defrost();
        // 为创建java对象
        //Class<?> testDataClass = testDataClassOfCtClass.toClass(application.getClass().getClassLoader());
        Class<?> testDataClass = testDataClassOfCtClass.toClass();
        //testDataClass.getAnnotations();

        //Object obj = testDataClass.newInstance();

   /*     // 为创建java对象
        Class<?> testDataClass = testDataClassOfCtClass.toClass();
        testDataClass.getAnnotations();
        Object obj = testDataClass.newInstance();*/
        Object obj = null;
        List<Object> list = new ArrayList<Object>();
        //5行数据
        //datas.subList()

        for (int i = 0; i < rows.size(); i++) {
            obj = testDataClass.newInstance();
            String fieldValue = "";
            int page = i+1;
            ListPageUtil<String> listPageUtil = new ListPageUtil<String>(datas,page , colTitleMaps.size());
            List<String> pagedList = listPageUtil.getPagedList();
            System.out.println(JSONObject.toJSONString(pagedList));
            //赋值
            for (int k = 0; k < fields.size(); k++) {

                if(k == 0){
                    fieldValue = rows.get(i);
                }else{
                    System.out.println(k-1);
                    fieldValue = pagedList.get(k-1);
                }

                String fieldName = fields.get(k);

                setFieldValue(obj,fieldName,fieldValue);
            }
            list.add(obj);
        }
     /*   //赋值
        for (int k = 0; k < fields.size(); k++) {
            String fieldName = fields.get(k);
            String fieldValue = fieldName.concat(RandomUtil.randomString(2));
            setFieldValue(obj,fieldName,fieldValue);
        }*/
        System.out.println("obj:"+JSONObject.toJSONString(obj));
        // 给动态添加的属性赋值
        //testDataClass.getMethod("setDynamicAddedFiled").invoke(testData,"testestet");

       // log.info("{}:{}",s,tempF.get(obj));
        // 导出excel
        //testDataClassOfCtClass.detach();


       // list.add(obj);
        log.info("data:{}", JSONObject.toJSONString(list));
        System.out.println("hahah"+JSONObject.toJSONString(list));
        String fileName = "C:\\Users\\EDY\\Desktop\\" + System.currentTimeMillis() + "++++++" + ".xlsx";

       // CaseDetailRspVo caseDetailRspVo = new CaseDetailRspVo();
       // EasyExcel.write(fileName, caseDetailRspVo.getClass()).sheet("动态添加属性").doWrite(list);
        EasyExcel.write(fileName, obj.getClass()).sheet("动态添加属性").doWrite(list);
    }



    private static  List<String>  setData() {
       List<String> objects = Lists.newArrayList();
        for (int i = 0; i < 20; i++) {
            objects.add(""+i);
        }
        return objects;
    }

    private static void extracted(/*String fieldValue, */ClassPool classPool
            , String fieldName, CtClass testDataClassOfCtClass, ConstPool constPool, List<String> titleNames) throws CannotCompileException, NotFoundException {
        // 增加属性，这里仅仅是增加属性字段
       // String fieldType = fieldValue.getClass().getName (); // 返回值的类型
        CtField ctField = new CtField(classPool.get(String.class.getName()), fieldName, testDataClassOfCtClass);
        ctField.setModifiers(Modifier.PRIVATE); // 设置字段的编码修饰符

        // 首先创建注解
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation annotation = new Annotation("com.alibaba.excel.annotation.ExcelProperty", constPool);

        //设置title名字,已经处理过合并单元格
        MemberValue[] memberValues = new MemberValue[titleNames.size()];
       /* StringMemberValue stringMemberValue1 = new StringMemberValue("模型（表）注册", constPool);
        StringMemberValue stringMemberValue2 = new StringMemberValue("模型"+i, constPool);
        memberValues = new MemberValue[]{stringMemberValue1, stringMemberValue2};*/
        for (int i = 0; i < titleNames.size(); i++) {
            memberValues[i] =  new StringMemberValue(titleNames.get(i), constPool);
        }

        ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constPool);
        arrayMemberValue.setValue(memberValues);
        annotation.addMemberValue("value", arrayMemberValue);
        annotationsAttribute.addAnnotation(annotation);

        // 在属性上添加注解
        ctField.getFieldInfo().addAttribute(annotationsAttribute);

        // 将字段添加到class中
        testDataClassOfCtClass.addField(ctField);

        // 添加方法
        String upperCasField = capitalizeFirstLetter(fieldName);
        String setStr = "public void set%s(String %s){this.%s = %s;}";
        String getStr = "public String get%s(){return this.%s;}";
        testDataClassOfCtClass.addMethod(CtNewMethod.make(String.format(setStr,upperCasField,fieldName,fieldName,fieldName), testDataClassOfCtClass));
        testDataClassOfCtClass.addMethod(CtNewMethod.make(String.format(getStr,upperCasField,fieldName), testDataClassOfCtClass));

    }

    private static Object setFieldValue(Object object, String fieldName, String val) throws Exception {
        Object result = null;
        Field fu = object.getClass().getDeclaredField(fieldName); // 获取对象的属性域
        // 设置对象属性域的访问属性
        fu.setAccessible(true);
        // 设置对象属性域的属性值
        ExcelProperty annotation = fu.getAnnotation(ExcelProperty.class);
        fu.set(object,val);
        // 获取对象属性域的属性值
        result = fu.get(object);
        return result;
    }


    public static String capitalizeFirstLetter(String str) {
        if (str == null || str.length() == 0) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }


    /**
     *
     * @param param
     * @return
     *  //  1_1_1  构建为 1   1_1   1_1_1
     */
    public static Set<String> genCol(String param) {
        Set<String> cols = Sets.newHashSet();
        for (int i = 1; i <= param.length(); i++) {
            if(i%2 ==0){
                continue;
            }
            cols.add(param.substring(0, i));
        }
        return cols;
    }

    public static long getColCount(List<Col> params) {
        if(CollectionUtil.isEmpty(params)){
            return 0;
        }
        int size = params.stream().collect(Collectors.groupingBy(v -> v.getColLevel().split("_")[0])).size();
        // long count = params.stream().map(p -> p.getColLevel().split("_")[0]).count();
        return size;
    }

    /**
     * 构建表头处理单元格是否合并
     * @param params 列表头数据以及格式
     * @return
     */
    public static LinkedHashMap<String, List<String>> getColTitle(List<Col> params) {
        if(CollectionUtil.isEmpty(params)){
            return null;
        }

        //col全量数据
        Map<String, String> colMap = params.stream()
                .collect(Collectors.toMap(Col::getColLevel, Col::getColName,(k1,k2) -> k1));

        //拿到 1  2  3  4...对应得数据 获取需要单元格合并得数据
        Map<String, List<Col>> sortedMap = params.stream().collect(Collectors.groupingBy(v -> v.getColLevel().split("_")[0]));
        // long count = params.stream().map(p -> p.getColLevel().split("_")[0]).count();
        // key升序排序
        sortedMap = sortedMap.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        // 解决可能存在的键冲突问题，默认保留第一个值
                        (oldValue, newValue) -> oldValue,
                        // 提供一个新的TreeMap实例作为收集器，用于保持排序
                        () -> new TreeMap<>()
                ));
        LinkedHashMap<String, List<String>> linkedHashMap = Maps.newLinkedHashMap();
        //列数控制
        AtomicInteger colCount = new AtomicInteger(1);
        //遍历第一列 第二列 ....  key为从1开始得整数
        sortedMap.forEach((key,value)->{
            //处理第key列单元格(单元格合并问题)

            //只有一列 不用考虑合并
            if(value.size() == 1){
                List<String> tempList = Lists.newArrayList();
                tempList.add(value.get(0).getColName());
                linkedHashMap.put(String.valueOf(colCount.get()),tempList);
                colCount.getAndIncrement();
                return;
            }
           /* if(value.size() == 2){
                // 1  1_1  直接拿到1_1
                Col col = value.stream().filter(v -> v.getColLevel().startsWith(key + "_")).findFirst().get();
                tempList.add(colMap.get(key));
                tempList.add(col.getColName());
               // linkedHashMap.put(key,tempList);
                linkedHashMap.put(String.valueOf(colCount.get()),tempList);
                colCount.getAndIncrement();
                return;
            }*/

            //以下考虑单元格合并问题  比如存在格式  1_1_1  1_1_2

            //level降序
            Collections.sort(value, (a, b) -> b.getColLevel().compareTo(a.getColLevel()));
            Col col = value.get(0);
            //获取_出现最长次数
            long longestCount = col.getColLevel().chars().filter(ch -> ch == '_').count();
            List<Col> longestList = value.stream().filter(cols -> cols.getColLevel().chars()
                    .filter(ch -> ch == '_').count() == longestCount).collect(Collectors.toList());
            //level升序
            Collections.sort(longestList, (a, b) -> a.getColLevel().compareTo(b.getColLevel()));
            // longestList有几个就有几个单元格需要合并
            longestList.forEach(tempCol -> {
                List<String> tempList = Lists.newArrayList();
                //  1_1_1  构建为 1   1_1   1_1_1
                Set<String> cols = genCol(tempCol.getColLevel());
                cols.forEach(s -> { tempList.add(colMap.get(s));});
                linkedHashMap.put(String.valueOf(colCount.get()),tempList);
                colCount.getAndIncrement();
            });
        });
        return linkedHashMap;
    }
    public static List<Col> setColData() {
        List<Col> objects = Lists.newArrayList();
        Col col1 = new Col();
        col1.setColName("模型（表）注册");
        col1.setColLevel("1");
        objects.add(col1);

        Col col2 = new Col();
        col2.setColName("本期新增/下线/累计数/注册率");
        col2.setColLevel("1_1");
        objects.add(col2);

        Col col3 = new Col();
        col3.setColName("模型（算法）注册 ");
        col3.setColLevel("2");
        objects.add(col3);

        Col col4 = new Col();
        col4.setColName("本期新增/下线/累计数/注册率");
        col4.setColLevel("2_1");
        objects.add(col4);



        Col col5 = new Col();
        col5.setColName("3");
        col5.setColLevel("3");
        objects.add(col5);

        Col col6 = new Col();
        col6.setColName("4");
        col6.setColLevel("4");
        objects.add(col6);

     /*   List<Col> objects = Lists.newArrayList();
        Col col1 = new Col();
        col1.setColName("【低效期】");
        col1.setColLevel("2_2_1");
        objects.add(col1);

        Col col2 = new Col();
        col2.setColName("衰退期/归档期/销毁期");
        col2.setColLevel("2_2");
        objects.add(col2);

        Col col3 = new Col();
        col3.setColName("【有效期】");
        col3.setColLevel("2_1_1");
        objects.add(col3);


        Col col4 = new Col();
        col4.setColName("生成期/活动期");
        col4.setColLevel("2_1");
        objects.add(col4);


        Col col5 = new Col();
        col5.setColName("数据全生命周期");
        col5.setColLevel("2");
        objects.add(col5);

        Col col6 = new Col();
        col6.setColName("表总数/其中有效期占比");
        col6.setColLevel("1");
        objects.add(col6);*/

      /*  Col col7 = new Col();
        col7.setColName("2_1_1");
        col7.setColLevel("2_1_1");
        objects.add(col7);


        Col col9 = new Col();
        col9.setColName("2_2_2");
        col9.setColLevel("2_2_2");
        objects.add(col9);

        Col col11 = new Col();
        col11.setColName("2_1_3");
        col11.setColLevel("2_1_3");
        objects.add(col11);

        Col col8 = new Col();
        col8.setColName("2_1_2");
        col8.setColLevel("2_1_2");
        objects.add(col8);
*/


        return objects;
    }


    @Data
    public static class Col {

        private String categroCode;
        private String colName;
        private String colLevel;
    }
}
